#include "stm32f4xx.h"
#include <stdint.h>
#include <stdio.h>
#include <stdbool.h>
#include "dht11.h"
#include "delay.h"


#define    DHT11_DelayMs(t)                      Delay_Ms(t)
#define    DHT11_DelayUs(t)                      Delay_Us(t)

#define    DHT11_PIN_HIGH                        1
#define    DHT11_PIN_LOW                         0

#define __DHT11_CONFIG_IO_INPUT(DHT11)   \
{\
    GPIO_InitTypeDef GPIO_InitStructure; \
    GPIO_InitStructure.GPIO_Mode     = GPIO_Mode_IN; \
    GPIO_InitStructure.GPIO_PuPd     = GPIO_PuPd_UP; \
    GPIO_InitStructure.GPIO_Speed    = GPIO_Speed_50MHz; \
    GPIO_InitStructure.GPIO_Pin      = DHT11->pin; \
    GPIO_Init(DHT11->port, &GPIO_InitStructure); \
}

#define __DHT11_CONFIG_IO_OUTPUT(DHT11)   \
{\
    GPIO_InitTypeDef GPIO_InitStructure; \
    GPIO_InitStructure.GPIO_Mode     = GPIO_Mode_OUT; \
    GPIO_InitStructure.GPIO_OType    = GPIO_OType_PP; \
    GPIO_InitStructure.GPIO_PuPd     = GPIO_PuPd_UP; \
    GPIO_InitStructure.GPIO_Speed    = GPIO_Speed_50MHz; \
    GPIO_InitStructure.GPIO_Pin      = DHT11->pin; \
    GPIO_Init(DHT11->port, &GPIO_InitStructure); \
}

#define __DHT11_IO_SET(DHT11, value) \
{\
    if (value == DHT11_PIN_HIGH) \
    GPIO_SetBits(DHT11->port, DHT11->pin); \
    else \
    GPIO_ResetBits(DHT11->port, DHT11->pin); \
}

#define    DHT11_IO_H(DHT11)                        {__DHT11_IO_SET(DHT11, DHT11_PIN_HIGH)}
#define    DHT11_IO_L(DHT11)                        {__DHT11_IO_SET(DHT11, DHT11_PIN_LOW)}
#define    DHT11_IO_IN(DHT11)                       GPIO_ReadInputDataBit(DHT11->port, DHT11->pin)


static uint8_t dht11_rst(DHT11_t *dht11)
{
    __DHT11_CONFIG_IO_OUTPUT(dht11);
    DHT11_IO_L(dht11);
    DHT11_DelayMs(20);
    DHT11_IO_H(dht11);
    DHT11_DelayUs(30);
    __DHT11_CONFIG_IO_INPUT(dht11);

    return 0;
}

static int dht11_check(DHT11_t *dht11)
{
    uint32_t ret = 0;

    while (DHT11_IO_IN(dht11) && (ret < 100))
    {
        ret++;
        DHT11_DelayUs(1);
    }

    if (ret >= 100)
        return -2;
    else
        ret = 0;

    while (!DHT11_IO_IN(dht11) && (ret < 100))
    {
        ret++;
        DHT11_DelayUs(1);
    }

    if (ret >= 100)
        return -3;

    return 0;
}

static int dht11_readbit(DHT11_t *dht11)
{
    uint32_t ret = 0;

    while (DHT11_IO_IN(dht11) && (ret < 100))
    {
        ret++;
        DHT11_DelayUs(1);
    }

    ret = 0;
    while (!DHT11_IO_IN(dht11) && (ret < 100))
    {
        ret++;
        DHT11_DelayUs(1);
    }

    DHT11_DelayUs(40);
    if (DHT11_IO_IN(dht11))
        return 1;
    else
        return 0;
}

static uint8_t dht11_readbyte(DHT11_t *dht11)
{
    uint8_t data;
    for (uint32_t i = 0; i < 8; i++)
    {
        data <<= 1;
        data |= dht11_readbit(dht11);
    }
    return data;
}

int dht11_init(DHT11_t *dht11, GPIO_TypeDef* port, uint32_t pin)
{
    if (!dht11->init)
        return -1;

    assert_param(IS_GPIO_ALL_PERIPH(port));
    if (port  == GPIOA)        { RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); }
    else if (port  == GPIOB)   { RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); }
    else if (port  == GPIOC)   { RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE); }
    else if (port  == GPIOD)   { RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE); }
    else if (port  == GPIOE)   { RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE); }
    else if (port  == GPIOF)   { RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE); }
    else if (port  == GPIOG)   { RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE); }
    else return -1;

    __DHT11_CONFIG_IO_OUTPUT(dht11);

    dht11->port = port;
    dht11->pin = pin;
    dht11->temperature = 0.0;
    dht11->humidity = 0.0;
    dht11->init = true;

    DHT11_IO_H(dht11);
    dht11_rst(dht11);

    return dht11_check(dht11);
}

int dht11_readvalue(DHT11_t *dht11)
{
    if (!dht11 || !dht11->init)
        return -1;

    uint8_t buff[5];
    dht11_rst(dht11);
    if (dht11_check(dht11) == 0)
    {
        for (uint8_t i = 0; i < 5; i++)
        {
            buff[i] = dht11_readbyte(dht11);
        }
        if (buff[0] + buff[1] + buff[2] + buff[3] == buff[4])
        {
            dht11->humidity = buff[0];
            dht11->temperature = buff[2];
        }
        else
            return -2;
    }
    return 0;
}
